import { DoubleSide, Object3DEventMap } from "three";
import { App, Point } from "../../Engine";
import Const from "../../application/constants/Const";
import Helper from "../../utils/Helper";
import StereoPolygon from "../models/StereoPolygon";
import { BaseTileMap } from "../../map/BaseTileMap";

/**
 * 挖方分析
 * 1. 把renderOrder设置为很小。渲染在底图平面上的多边形，关闭颜色缓存区的写入，开启模板缓冲区。
 * 2. 把renderOrder设置为稍大。渲染场景，整个场景用模板缓冲区中的数据做剔除检测 
 * 3. 把renderOrder设置为最大。渲染挖方的mesh
 * 目前用的方案其实有缺陷，因为当相机的角度发生改变后，模版测试会把不该剔除掉的物体也剔除了。后期统一修改了材质后，可以把第一步和第二步结合成一步，在fragment shader中检测顶点是否在多边形内部，然后discard掉
 */
export default class DigAnalysis {
    private app:App;
    private data;
    private polygon;
    private stereoPolygon;
    private isOpenAnalysis: boolean;
    private afterLoadTileListener;
    private cutData;
    private positionArray;

    /**
     * 挖方分析
     */
    public constructor() {
        this.isOpenAnalysis = false;
       this.app = App.getInstance();
    }

    /**
     * 开启挖方分析
     * @param data 挖方分析需要用到的数据
     * @param data.pointArray 挖方分析的多边形数组
     * @param data.deep 挖方的深度
     * @param data.textureImg 挖方墙壁用到的纹理
     * @param data.textureRoofImg 挖方底部用到的纹理
     * @example let img = '/examples/resources/images/digAreaWall.jpg';
        let pointArray = [ //3857表示web墨卡托坐标系，4326表示经纬度坐标
            new GE.Point(13376713.90129997, 3540579.2959903134, 0, '3857'),
            new GE.Point(13376711.98134067, 3539826.5375406537, 0, '3857'),
            new GE.Point(13377952.49603797, 3539759.3270052206, 0, '3857'),
            new GE.Point(13378008.185227688, 3540675.3111583497, 0, '3857'),
            new GE.Point(13377184.375437068, 3540339.2581293588, 0, '3857')
        ];
        let digData = {
            pointArray: pointArray,  //多边形的顶点数组
            textureImg: img, //贴在挖方墙壁上的图片
            textureRoofImg: img, //贴在挖方底部的图片
            deep: -1000 //挖方的深度
        };
        digAnalysis.openAnalysis(digData);
     */
    public openAnalysis(data: { pointArray: Array<Point>, deep: number, textureImg?: string, textureRoofImg?: string }){
        if (this.isOpenAnalysis) { //挖方分析本来就在开着
            return;
        }
        this.data = data;
        
        this.afterLoadTileListener = this.afterLoadTileEvent.bind(this);
        this.positionArray = [];
        for (let i = 0; i < this.data.pointArray.length; i++) {
            let currentElement = this.data.pointArray[i];
            this.positionArray.push(currentElement.x, currentElement.y);
        }

        if (!this.data.pointArray[0].equals(this.data.pointArray[this.data.pointArray.length - 1])) {
            this.positionArray.push(this.data.pointArray[0].x, this.data.pointArray[0].y); //形成首尾相连的闭环
        }

        this.cutData = {isOpenClap: true, clapPointArray: this.positionArray, clapPointCount: this.positionArray.length, renderSide: DoubleSide};
        this.updateLayerCut(this.cutData);
        this.renderDigArea(this.data); //渲染挖方的mesh

        this.isOpenAnalysis = true;
    }

    /**
     * 关闭挖方分析
     */
    public closeAnalysis(){
        if(this.stereoPolygon){
            this.app.remove(this.polygon);

            this.stereoPolygon.dispose();
            this.stereoPolygon = null;

            this.cutData.isOpenClap = false;
            this.updateLayerCut(this.cutData);

            this.isOpenAnalysis = false;
        }
    }

    private renderDigArea(data) {
        data.renderOrder = Const.RenderOrder.DigArea;
        data.height = data.deep;
        this.stereoPolygon = new StereoPolygon(data);
    }
    
    private updateLayerCut(cutData){
        Helper.traverseObject(this.app.three.scene, function (obj) {
            try {
                if (obj.type == 'Mesh') {
                    if(obj.material.length > 1){
                        obj.material.forEach(element => {
                            if(element.material){
                                element.material.setCut(cutData);
                            }
                        });
                    }else{
                        if(obj.material){
                            obj.material.setCut(cutData);
                        }
                    }
                }
                return true;
            } catch (error) {
            }
        });

        let layerMap = this.app.map.getAllLayer();
        for (let type in layerMap) {
            // if(type != Const.tileType.imageType){ //除了影像底图，其他的图层都参与压平分析。之所以影像底图不参与，是为了避免在压平的边缘出现白线，说到底，这是浮点精度造成的问题

                let mapLayers = this.app.map.getLayersByType(type);
                let self = this;
                mapLayers.forEach((layer: BaseTileMap) => {
                    if(cutData.isOpenClap){
                        layer.addEventListener('afterLoadTile' as keyof Object3DEventMap, self.afterLoadTileListener);
                    }else{
                        layer.removeEventListener('afterLoadTile' as keyof Object3DEventMap, self.afterLoadTileListener);
                    }
                });
            // }
        }
    }

    private afterLoadTileEvent(eventData){
        let newTile = eventData.message;
        newTile.setCut(this.cutData);
    }
}